/*
 * lab3chq19.c
 *
 *  Created on: Mar 6th, 2011
 *      Author: Vishal Verma
 */

/*
 * PROGRAM DESCRIPTION
 *
 * Interrupt driven serial port driver with a FIFO
 *
 */

/*
 * COMPILER FLAGS
 * sdcc -c --debug -mmcs51 --std-sdcc99 --model-large
 *
 * LINKER FLAGS
 * sdcc --debug -mmcs51 --code-loc 0x0000 --code-size 0x8000 --xram-loc 0x0000 --xram-size 0x8000 --model-large --out-fmt-ihx
 *
 * USAGE SPECIFICATIONS:
 * Baud rate - 57600
 */

/*==============================================INCLUDES=========================================*/
#include <at89c51ed2.h>  //also includes 8052.h and 8051.h
#include <mcs51reg.h>
#include <stdio.h>
#include <stdlib.h>

/*==============================================DEFINES==========================================*/
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#define PB_SWITCH P1_5
#define LED P1_7
#define BUFSIZE 100
#define TESTBUFSIZE 5


/*==============================================GLOBALS==========================================*/

enum RETCODE{							//retcodes/parameter flags for all functions
	STDOK = 0,
	STDERR = -1
};

enum ERRNO{
	EINVAL = 1,
	EWOULDBLOCK,
	EEMPTY,
	EFULL
};

unsigned int errno = 0;

char rxBuf[BUFSIZE] = {0};
int rxTail = 0;
int rxCount = 0;

char txBuf[BUFSIZE] = {0};
int txTail = 0;
int txCount = 0;

char testBuf[TESTBUFSIZE] = {0};

/*=========================================FUNCTION PROTOTYPES===================================*/

void putchar (char c);
char getchar (void);
int putstr (char *s);
int getstr (char *s);
void serialInit(int);
void clearScr(void);
void myDelay(int j);
void initAll(void);

void printMenu(void);
void cleanTBuf(void);
void cout(char *buf, int size);
int cin (char *buf, int size);
int txPeek(void);
int rxPeek(void);
void txEnqueue(char ch);
int txDequeue(void);
void rxEnqueue(char ch);
int rxDequeue(void);



/*=========================================FUNCTION DEFINITIONS===================================*/

/*
* NAME :            _sdcc_external_startup
*
* DESCRIPTION :     Will run before main(). Used to initialize registers etc. as required
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	AUXR
*
* OUTPUTS :
*       GLOBALS:	AUXR
*       RETURN:		-
*
*/

_sdcc_external_startup() {

	AUXR |= 0x0C;
	return 0;
}

/*
* NAME :            putchar
*
* DESCRIPTION :     print 1 character to serial port
*
* INPUTS :
*       PARAMETERS:	char c
*		GLOBALS:	SCON
*
* OUTPUTS :
*       GLOBALS :	SBUF, TI
*       RETURN :	-
*
* PROCESS :
*                   [1]	Wait for port to be ready
* 					[2]	Print char and clear TI
*/

void putchar (char c) {

	/*while (0 == ( BUFSIZE - txPeek() ));
	cout(&c, 1);*/

	SBUF = c;  	                // load serial port with transmit value
	while (!TI);	            // check T1 flag
	TI = 0;  	                // clear TI flag
}

/*
* NAME :            getchar
*
* DESCRIPTION :     get 1 character from serial port
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	SCON
*
* OUTPUTS :
*       GLOBALS :	SBUF, RI
*       RETURN :	SBUF - character received
*
* PROCESS :
*                   [1]	Wait for character to be received
* 					[2]	Clear RI
*					[3] Return character
*/

char getchar () {

	char ch;

	while (0 == rxPeek());                		// Wait until we receive at least 1 char
	cin( &ch, 1 );  	            			// return character from SBUF
	return ch;

/*
	while ((SCON & 0x01) == 0);  				// wait for character to be received, spin on RI
	RI = 0;										// clear RI flag
	return SBUF;  								// return character from SBUF
*/
}

/*
* NAME :            putstr
*
* DESCRIPTION :     Print a string to serial port
*
* INPUTS :
*       PARAMETERS:	char *s
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	Number of characters transmitted
*
* PROCESS :
*                   [1]  Transmit characters till oxoo (NULL) is encountered
*/

int putstr (char *s) {

	int i = 0;
	while (*s){									// output characters until NULL found
		putchar(*s++);
		i++;
	}
	//putchar('\n\r');
	return i+1;
}

/*
* NAME :            getstr
*
* DESCRIPTION :     Get a string from serial port
*
* INPUTS :
*       PARAMETERS:	char *s
*		GLOBALS:	-
*
* OUTPUTS :
*		PARAMETERS: char *s
*       GLOBALS :	-
*       RETURN :	i - number of characters read
*
* PROCESS :
*                   [1]  Accept characters till 0x0D encountered (CR)
*					[2]	 Store starting at given location (*s)
*
* NOTES :           Is not safe from buffer overrun. Assumes valid memory available for all characters input.
*					Can be substituted with gets() which is equally unsafe but I wrote this so am going with it :)
*/

int getstr (char *s) {

	int i = 0;
	do {
		s[i] = getchar();
		putchar(s[i]);
		i++;
	} while(s[i-1] != 0x0D);  					//0x0D is <CR>
	s[i-1] = 0x00;								//replace <CR> with NULL (0x00)
	putchar('\n\r');
	return (i-1);
}

/*
* NAME :            serialInit
*
* DESCRIPTION :     Initialize the serial port
*
* INPUTS :
*       PARAMETERS:	int br (BAUD RATE)
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	PCON, SCON, TMOD, TH1, TR1, TI
*       RETURN :	-
*
*
* NOTES :  Baud rate is supplied as 0xFF for 56700 etc
*/

void serialInit(int br) {

/*
	PCON |= 0x80;								//SMOD1 = 1
	SCON = 0x50;								//uart in mode 1 (8 bit), REN=1
	BDRCON &=0xEC;								//BRR=0; SRC=0;
	BDRCON |=0x0E;								//TBCK=1;RBCK=1; SPD=1
	BRL = br;
	BRL=0xFD;									//115200 Bds at 11.059MHz
	EA = 1;										//Enable serial interrupt
	BDRCON |= 0x10;								//Baud rate generator run
*/


	PCON |= 0x80;								//configure uart, fast baud
	SCON = 0x50;                   			 	//SCON: mode 1, 8-bit UART, enable rcvr
	TMOD |= 0x20;                   			//TMOD: timer 1, mode 2, 8-bit reload for serial
	TMOD |= 0x01;                   			//TMOD: timer 1, mode 2, 8-bit reload for debounce
	TH1 = br;                   				//TH1:  reload value for baudrate
	TR1 = 1;                      				//TR1:  timer 1 run
	TI = 0;
	ES = 1;										//enable UART interrupt
}

/*
* NAME :            initAll
*
* DESCRIPTION :     Reset global variables to initial values
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*/

void initAll() {

	int i = 0;

	for (i=0; i<BUFSIZE; i++) {
		rxBuf[i] = 0;
		txBuf[i] = 0;
	}
	rxTail = 0;
	txTail = 0;
	txCount = 0;
	rxCount = 0;
}

/*
* NAME :            clearScr
*
* DESCRIPTION :     Clears and resets the VT100 terminal
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*
*
* NOTES :           Will clear the VT100 terminal by sending control sequences to reset and clear entire screen
*					Reset: ESC c
*					Clear Screen: ESC [ 2 J
*/

void clearScr() {
	printf("\033c");			//reset terminal
	printf("\033[2J");			//clear screen
}

/*
* NAME :            myDelay
*
* DESCRIPTION :     Provide a delay
*
* INPUTS :
*       PARAMETERS:	int j - empirical - j=1 ~ 1 second
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*/

void myDelay(int j) {
	int i = 0;
	int cnt = 0;

	j*=100;
	while(j) {
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		j--;
	}

}

/*
* NAME :            printMenu
*
* DESCRIPTION :     prints menu
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*		PARAMETERS: -
*       GLOBALS :	-
*       RETURN :	-
*/

void printMenu() {
	clearScr();
	putstr("***********************************WELCOME**********************************\n\r");
	putstr("*                                                                          *\n\r");
	putstr("* This program implements an interrupt based serial driver and a FIFO      *\n\r");
	putstr("*                                                                          *\n\r");
	putstr("****************************************************************************\n\n\n\r");

	putstr("1-> Test RX with FIFO buffer \n\r");
	putstr("2-> Test TX with FIFO buffer \n\r");
	putstr("\n\r");
	putstr("****************************************************************************\n\r\n\r");
	putstr("Command > ");
}

/*
* NAME :            cleanTBuf
*
* DESCRIPTION :     cleans tmp buffer
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	testBuf
*
* OUTPUTS :
*		PARAMETERS: -
*       GLOBALS :	testBuf
*       RETURN :	-
*/

void cleanTBuf() {

	int i = 0;

	for (i=0; i<TESTBUFSIZE; i++) {
		testBuf[i] = 0;
	}
}

/*
* NAME :            uartVect
*
* DESCRIPTION :     ISR for UART interrupt
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]  Check if receive interrupt
*					[2]  Fill queue with SBUF
*					[3]  Check if transmit interrupt
*					[4]  Dequeue into SBUF
*
* NOTES :
*/

void uartVect(void) __interrupt (SI0_VECTOR) {		//external interrupt ISR1 routine

	(LED) ? (LED = OFF) : (LED = ON);				// toggle LED

	if (RI == 1) {
		RI = 0;
		rxEnqueue(SBUF);
	}
	else if(TI == 1) {
		//TI = 0;
		if(txPeek()) {
			TI = 0;
			SBUF = txDequeue();
		}
	}

	return;
}

/*
* NAME :            cout
*
* DESCRIPTION :     User visible function to output data using serial buffer
*
* INPUTS :
*       PARAMETERS:	buf, size
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]  enqueue into transmit queue one by one
*
* NOTES :
*/

void cout (char *buf, int size) {
	int i = 0;

	TI = 1;
	for (i=0; i<size; i++) {
		txEnqueue(buf[i]);
	}

}

/*
* NAME :            cin
*
* DESCRIPTION :     User visible function to input data using serial buffer
*
* INPUTS :
*       PARAMETERS:	buf, size
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]  if queue has data, dequeue upto size provided and pass back
*
* NOTES :
*/

int cin (char *buf, int size) {

	int i = 0;
	int rc = 0;

	errno = STDOK;

	if (!rxPeek()) {
		errno = EEMPTY;
		return STDERR;
	}

	if (size == 1) {
		*buf = rxDequeue();
		return 1;
	}

	for(i=0; i<size; i++) {
		rc = rxDequeue();
		//printf("ch = %c, i = %d\n\r", rc, i);
		if (rc == STDERR) {
			//printf("returning %d\n\r", i);
			return i;
		}
		buf[i] = (char) rc;
	}

	return i;
}

/*
* NAME :            txPeek
*
* DESCRIPTION :     return size of tx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

int txPeek() {
	return txCount;
}

/*
* NAME :            rxPeek
*
* DESCRIPTION :     return size of rx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

int rxPeek() {
	return rxCount;
}

/*
* NAME :            txEnqueue
*
* DESCRIPTION :     enqueue data into the tx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

void txEnqueue(char ch) {

	int idx = 0;

	if (txCount < BUFSIZE) {		//check for queue full but discard silently
		idx = (txTail + txCount) % BUFSIZE;
		txBuf[idx] = ch;
		txCount++;
	}
}

/*
* NAME :            txDequeue
*
* DESCRIPTION :     Dequeue dats from the tx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

int txDequeue(void) {

	char ch = 0;

	errno = STDOK;

	if (txCount <= 0) {
		errno = EEMPTY;
		return STDERR;
	}
	else {
		ch = txBuf[txTail];
		txTail++;
		txTail %= BUFSIZE;
		txCount--;
	}

	return (int) ch;
}

/*
* NAME :            rxEnqueue
*
* DESCRIPTION :     enqueue data into the rx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

void rxEnqueue(char ch) {

	int idx = 0;

	if (rxCount < BUFSIZE) {		//check for queue full but discard silently
		idx = (rxTail + rxCount) % BUFSIZE;
		rxBuf[idx] = ch;
		rxCount++;
	}
}

/*
* NAME :            rxDequeue
*
* DESCRIPTION :     Dequeue dats from the rx queue
*
* INPUTS :
*       PARAMETERS:
*		GLOBALS:
*
* OUTPUTS :
*		PARAMETERS:
*       GLOBALS :
*       RETURN :
*
* PROCESS :
*                   [1]
*
* NOTES :
*/

int rxDequeue(void) {

	char ch = 0;

	errno = STDOK;

	if (rxCount <= 0) {
		errno = EEMPTY;
		return STDERR;
	}
	else {
		ch = rxBuf[rxTail];
		rxTail++;
		rxTail %= BUFSIZE;
		rxCount--;
	}

	return (int) ch;
}

/*=========================================FUNCTION MAIN==========================================*/

void main() {

	int rc = 0;
	int i = 0;
	char cmd = 0;
	int escFlag = 0;
	char outBuf[] = "Testing cout\n\r";  //15

	serialInit(0xFF); 				//0xFF gives Baud Rate 57600
	initAll();
	//putchar('a');
	//getchar();
	clearScr();
	EA = 1;
	LED = ON;						//turn on LED

	//printMenu();

	while(1) {

		printMenu();
		cmd = getchar();
		putchar(cmd);

		/*cleanTBuf();
		rc = cin(testBuf, TESTBUFSIZE-1);
		if (rc > 0) {
			testBuf[rc] = 0;
			printf("[%d] Buffer received: %s\n\r", rc, testBuf);
		}
		myDelay(2);*/

		switch (cmd) {
			case '1':
				clearScr();
				putstr("INSTRUCTIONS: \n\r\n\r");

				putstr("This will demonstrate RX using the interrupt based FIFO serial Buffer\n\r");
				putstr("You can type continuously, the program will read from the buffer in \n\r");
				putstr("chunks of 4 characters and display them on the screen\n\r\n\r");

				//putstr("At any time press ESC to exit\n\r\n\r");

				putstr("Press any key to continue: \n\r");
				rc = getchar();
				if(rc == 27) {
					break;
				}

				clearScr();
				putstr("RX TEST ON:\n\r");

				while(1) {
					cleanTBuf();
					rc = cin(testBuf, TESTBUFSIZE-1);
					if (rc > 0) {
						testBuf[rc] = 0;
						printf("[%d] Buffer received: %s\n\r", rc, testBuf);
						/*for (i=0; i<TESTBUFSIZE; i++) {
							if (testBuf[i] == 27) escFlag = 1;
						}*/
					}
					/*if(escFlag) {
						escFlag = 0;
						break;
					}*/
					myDelay(2);
				}
				break;

			case '2':
				clearScr();
				putstr("INSTRUCTIONS: \n\r\n\r");

				putstr("This will demonstrate RX using the interrupt based FIFO serial Buffer\n\r");
				putstr("You can type continuously, the program will read from the buffer in \n\r");
				putstr("chunks of 4 characters and display them on the screen\n\r\n\r");

				putstr("At any time press ESC to exit\n\r\n\r");

				putstr("Press any key to continue: \n\r");
				rc = getchar();
				if(rc == 27) {
					break;
				}

				clearScr();
				putstr("TX TEST ON:\n\r");

				while(1) {
					cout (outBuf, 15);
					/*if (rxPeek) rc = cin(testBuf, 1);
					if (rc == 1) {
						break;
					}*/
					//myDelay(1);
				}
				break;

			default:
				clearScr();
				putstr("Invalid Entry, Try again! \n\r");
				myDelay(1);
		}
	}
}

